// Welcome, to horror.

// No include guard, because we're crazy.

#ifndef FPK_THREAD_INTERNAL_ARGS_NUMBER
#error You're doing it wrong.
#endif

#ifndef FPK_THREAD_INTERNAL_THIS_HAS
#error You're doing it wrong.
#endif

#if FPK_THREAD_INTERNAL_THIS_HAS
#define FPK_THREAD_INTERNAL_THIS_TEMPLATEARG typename ArgThis
#define FPK_THREAD_INTERNAL_THIS_FUNCTIONSCOPE ArgThis::
#define FPK_THREAD_INTERNAL_THIS_UINT32ARG UINT32 argThis
#define FPK_THREAD_INTERNAL_THIS_RECEIVE Receive<ArgThis&>(argThis).*
#define FPK_THREAD_INTERNAL_THIS_ARG ArgThis argThis
#define FPK_THREAD_INTERNAL_THIS_PASS Pass(argThis)
#else
#define FPK_THREAD_INTERNAL_THIS_TEMPLATEARG 
#define FPK_THREAD_INTERNAL_THIS_FUNCTIONSCOPE 
#define FPK_THREAD_INTERNAL_THIS_UINT32ARG 
#define FPK_THREAD_INTERNAL_THIS_RECEIVE 
#define FPK_THREAD_INTERNAL_THIS_ARG 
#define FPK_THREAD_INTERNAL_THIS_PASS 
#endif

#if (FPK_THREAD_INTERNAL_THIS_HAS || (FPK_THREAD_INTERNAL_ARGS_NUMBER > 0))
#define FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR , 
#else
#define FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR 
#endif

#if (FPK_THREAD_INTERNAL_THIS_HAS && (FPK_THREAD_INTERNAL_ARGS_NUMBER > 0))
#define FPK_THREAD_INTERNAL_THISARGS_SEPERATOR , 
#else
#define FPK_THREAD_INTERNAL_THISARGS_SEPERATOR 
#endif

#if (FPK_THREAD_INTERNAL_ARGS_NUMBER == 0)
#define FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS 
#define FPK_THREAD_INTERNAL_ARGS_ARGS 
#define FPK_THREAD_INTERNAL_ARGS_UINT32ARG 
#define FPK_THREAD_INTERNAL_ARGS_RECEIVE 
#define FPK_THREAD_INTERNAL_ARGS_PASS 
#elif (FPK_THREAD_INTERNAL_ARGS_NUMBER == 1)
#define FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS typename Arg0
#define FPK_THREAD_INTERNAL_ARGS_ARGS Arg0 arg0
#define FPK_THREAD_INTERNAL_ARGS_UINT32ARG UINT32 arg0
#define FPK_THREAD_INTERNAL_ARGS_RECEIVE Receive<Arg0>(arg0)
#define FPK_THREAD_INTERNAL_ARGS_PASS Pass(arg0)
#elif (FPK_THREAD_INTERNAL_ARGS_NUMBER == 2)
#define FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS typename Arg0, typename Arg1
#define FPK_THREAD_INTERNAL_ARGS_ARGS Arg0 arg0, Arg1 arg1
#define FPK_THREAD_INTERNAL_ARGS_UINT32ARG UINT32 arg0, UINT32 arg1
#define FPK_THREAD_INTERNAL_ARGS_RECEIVE Receive<Arg0>(arg0), Receive<Arg1>(arg1)
#define FPK_THREAD_INTERNAL_ARGS_PASS Pass(arg0), Pass(arg1)
#elif (FPK_THREAD_INTERNAL_ARGS_NUMBER == 3)
#define FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS typename Arg0, typename Arg1, typename Arg2
#define FPK_THREAD_INTERNAL_ARGS_ARGS Arg0 arg0, Arg1 arg1, Arg2 arg2
#define FPK_THREAD_INTERNAL_ARGS_UINT32ARG UINT32 arg0, UINT32 arg1, UINT32 arg2
#define FPK_THREAD_INTERNAL_ARGS_RECEIVE Receive<Arg0>(arg0), Receive<Arg1>(arg1), Receive<Arg2>(arg2)
#define FPK_THREAD_INTERNAL_ARGS_PASS Pass(arg0), Pass(arg1), Pass(arg2)
#endif

/**
 *  OK... because we don't have variadic template parameters, we have to partially specialize the Thread template class to handle various function types... using macros.
 *  
 *  If you have no idea what we just said, you should just wipe this from your mind and just look at the normal documentation for Thread.
 */
template<FPK_THREAD_INTERNAL_THIS_TEMPLATEARG FPK_THREAD_INTERNAL_THISARGS_SEPERATOR FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS>
class Thread<void (FPK_THREAD_INTERNAL_THIS_FUNCTIONSCOPE*)(FPK_THREAD_INTERNAL_ARGS_ARGS)> :
	public ThreadBase
{
	typedef void (FPK_THREAD_INTERNAL_THIS_FUNCTIONSCOPE*Function)(FPK_THREAD_INTERNAL_ARGS_ARGS);
	typedef void (*FirstFunction)(UINT32 function FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR FPK_THREAD_INTERNAL_THIS_UINT32ARG FPK_THREAD_INTERNAL_THISARGS_SEPERATOR FPK_THREAD_INTERNAL_ARGS_UINT32ARG);
	
	private:
		const Function m_function;
		
		static void Run(UINT32 function FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR FPK_THREAD_INTERNAL_THIS_UINT32ARG FPK_THREAD_INTERNAL_THISARGS_SEPERATOR FPK_THREAD_INTERNAL_ARGS_UINT32ARG)
		{
			(FPK_THREAD_INTERNAL_THIS_RECEIVE(Receive<Function>(function)))(FPK_THREAD_INTERNAL_ARGS_RECEIVE);
		}
	public:
		Thread(const std::string &name, Function function) :
			ThreadBase(name, Run),
			m_function(function)
		{
		}
		
		void Start(FPK_THREAD_INTERNAL_THIS_ARG FPK_THREAD_INTERNAL_THISARGS_SEPERATOR FPK_THREAD_INTERNAL_ARGS_ARGS)
		{
			m_task.Start(Pass(m_function) FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR FPK_THREAD_INTERNAL_THIS_PASS FPK_THREAD_INTERNAL_THISARGS_SEPERATOR FPK_THREAD_INTERNAL_ARGS_PASS);
		}
};

// Undefine our crazy macros because we're not THAT crazy.
#undef FPK_THREAD_INTERNAL_THIS_TEMPLATEARG 
#undef FPK_THREAD_INTERNAL_THIS_FUNCTIONSCOPE 
#undef FPK_THREAD_INTERNAL_THIS_UINT32ARG 
#undef FPK_THREAD_INTERNAL_THIS_RECEIVE 
#undef FPK_THREAD_INTERNAL_THIS_ARG 
#undef FPK_THREAD_INTERNAL_THIS_PASS 
#undef FPK_THREAD_INTERNAL_FUNCTIONARGS_SEPERATOR 
#undef FPK_THREAD_INTERNAL_THISARGS_SEPERATOR 
#undef FPK_THREAD_INTERNAL_ARGS_TEMPLATEARGS 
#undef FPK_THREAD_INTERNAL_ARGS_ARGS 
#undef FPK_THREAD_INTERNAL_ARGS_UINT32ARG 
#undef FPK_THREAD_INTERNAL_ARGS_RECEIVE 
#undef FPK_THREAD_INTERNAL_ARGS_PASS 
#undef FPK_THREAD_INTERNAL_ARGS_NUMBER 
#undef FPK_THREAD_INTERNAL_THIS_HAS 
